package thread;

//通过数组实现环形队列

//线程进行修改数据，导致线程可能出现不安全的问题
//1.加锁
class MyBlockingQueue {
    private String[] data = new String[1000];

    //下面的代码中，一直在修改数据
    //为了避免指令重排序问题，应该用volatile修饰
    private volatile int size = 0;
    private volatile int head = 0;
    private volatile int tail = 0;
    public void put(String elem) throws InterruptedException {
        synchronized (this){
            while (size == data.length){

                //此时如果线程满了，应该阻塞线程，等到线程中的数据被拿出，拥有位置之后被唤醒


                //使用wait唤醒时，要考虑这个线程是不是仍是满这的
                //如果通过notify唤醒，说明其他线程调用了take，此时队列不满了，可以继续添加元素
                //但是interrupt也可以唤醒线程，如果让他唤醒，会导致数组仍让是满的，再进行操作后续会出现问题
                //因此我们再唤醒之后应该判断队列是不是满的，如果是则继续等待
                //但是此时出现一个新的问题，每次唤醒都需要if判断，满则等待，不满则继续
                //会重复写相同的语句，并且无法中止
                //解决办法就是：用while循环代替if
                //这个解决办法是java文档官方推荐写法
                this.wait();
            }
            data[tail] = elem;
            tail++;

            if(tail == data.length){
                tail = 0;
            }
            size++;
            this.notify();

        }

    }
    public String take() throws InterruptedException {
        synchronized (this){
            if(size == 0){
                //如果线程为空，无法take，此时让线程等待，直到线程put数据被唤醒
                this.wait();
            }
            String ret = data[head];
            head++;

            if(head==data.length){
                head=0;
            }
            size--;
            //如果线程满了，这里有空闲的位置，可以从这里唤醒wait中的线程
            this.notify();
            return  ret;
        }

    }

}
public class demo26 {
    public static void main(String[] args) {
        MyBlockingQueue myBlockingQueue = new MyBlockingQueue();
        //消费者
        Thread t1 = new Thread(()->{
            while (true){
                try {
                    String res = myBlockingQueue.take();
                    System.out.println("消费者:" + res);
                    Thread.sleep(500);

                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        //生产者
        Thread t2 = new Thread(()->{
            int num = 1;
            while (true){
                try {
                    myBlockingQueue.put(num+"");
                    System.out.println("生产者:" +num);
                    num++;
                    //Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        });

        t1.start();
        t2.start();
    }

}
